use anyhow::Result;
use encoding_rs::*;
use macaddr::MacAddr6;
use std::{collections::HashMap, fmt::Display, str::FromStr};

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BandWidth {
    HT20,
    HT40,
    MHz80,
    MHz160,
}

impl Default for BandWidth {
    fn default() -> Self {
        Self::HT20
    }
}

impl Display for BandWidth {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.cmd())
    }
}

impl BandWidth {
    pub fn cmd(&self) -> &'static str {
        match self {
            BandWidth::HT20 => "HT20",
            BandWidth::HT40 => "HT40",
            BandWidth::MHz80 => "80MHz",
            BandWidth::MHz160 => "160MHz",
        }
    }
}

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SecondChannel {
    Below,
    Above,
}

#[derive(Debug, Clone)]
pub struct BSS {
    pub channel: i32,
    pub freq_mhz: i32,
    pub rssi: i32,
    pub ssid: String,
    pub width: BandWidth,
    pub second: Option<SecondChannel>,
    pub addr: MacAddr6,
    pub std: Standard,
    pub security: Vec<Security>,
}

#[repr(C)]
#[derive(Debug, Clone, Copy, Hash)]
pub enum Standard {
    B,
    A,
    G,
    N,
    AC,
    AX,
}

#[repr(C)]
#[derive(Debug, Clone, Copy, Hash)]
pub enum Security {
    WEP,
    WPA,
    WPA2,
    WPA3,
}

impl Display for BSS {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            r"BSS
    ssid: {}
    mac: {}
    channel: {}
    freq: {} MHz
    rssi: {}
    width: {}
    second: {:?}
    std: 802.11{:?}
    security: {:?}",
            self.ssid,
            self.addr,
            self.channel,
            self.freq_mhz,
            self.rssi,
            self.width,
            self.second,
            self.std,
            self.security,
        )
    }
}

impl BSS {
    pub(crate) fn parse_all(result: &str) -> Result<Vec<Self>> {
        let mut out = Vec::new();
        let mut iter = 0;

        while let Some(_n) = result[iter..].find("\nBSS") {
            let mut end = result.len() - 1;
            if let Some(n) = result[iter + 5..].find("\nBSS ") {
                end = iter + n + 5;
            }
            let bss_str = &result[iter..end];

            if let Some(s) = Self::parse2(bss_str) {
                out.push(s);
            }

            iter = end;
        }

        Ok(out)
    }

    fn parse2(src: &str) -> Option<Self> {
        let addr = {
            let mut line = src.trim();
            if let Some(n) = line.find("BSS") {
                if n < 10 {
                    line = &line[n + 4..];
                }
            }
            line = line.trim();
            if let Some(n) = line.find("(") {
                line = &line[..n];
            }
            MacAddr6::from_str(line).unwrap_or_default()
        };

        let sections = prase_section(src, 1);

        let freq_mhz = sections
            .get("freq")
            .map_or(0, |s| s.trim().parse::<i32>().unwrap());

        let rssi = sections.get("signal").map_or(-127, |src| {
            let src = src.trim_end_matches("dBm").trim();
            let rssi = src.parse::<f32>().unwrap();
            rssi as i32
        });

        let ssid = sections
            .get("SSID")
            .map_or(String::new(), |s| parse_ssid(s.trim()));

        let mut channel = sections.get("DS Parameter set").map_or(0, |src| {
            let c = src.trim().trim_start_matches("channel").trim();
            c.parse().unwrap()
        });
        let mut second = None;

        if let Some(src) = sections.get("HT operation") {
            let sub = prase_section(src, 2);

            if let Some(pri) = sub.get("* primary channel") {
                let c = pri.trim();
                if let Ok(ch) = c.parse() {
                    channel = ch;
                }
            }

            if let Some(s) = sub.get("* secondary channel offset") {
                second = match s.trim() {
                    "above" => Some(SecondChannel::Above),
                    "below" => Some(SecondChannel::Below),
                    _ => None,
                }
            }
        }

        let mut width = BandWidth::HT20;

        if freq_mhz >= 5000 {
            if src.contains("short GI (80 MHz)") {
                width = BandWidth::MHz80;
            }
            if src.contains("short GI (160 MHz)") {
                width = BandWidth::MHz80;
            }
        } else if second.is_some() {
            width = BandWidth::HT40;
        }

        let mut standard = Standard::B;
        let mut security = Vec::new();

        if let Some(rate) = sections.get("Supported rates") {
            if rate.contains("11.0") {
                standard = Standard::A;
            }
        }

        if sections.contains_key("HT capabilities") {
            standard = Standard::G;
        }

        if freq_mhz >= 5000 {
            standard = Standard::N
        }

        if sections.contains_key("VHT capabilities") {
            standard = Standard::AC;
        }
        if sections.contains_key("HE capabilities") {
            standard = Standard::AX;
        }

        if sections.contains_key("WEP") {
            security.push(Security::WEP);
        }

        if sections.contains_key("WPA") {
            security.push(Security::WPA);
        }

        if sections.contains_key("RSN") {
            security.push(Security::WPA2);
        }

        Some(Self {
            channel,
            freq_mhz,
            rssi,
            ssid,
            width,
            second,
            addr,
            std: standard,
            security,
        })
    }
}

// fn search_key_value<'a>(src: &'a str, key: &str) -> Option<&'a str> {
//     let i_key_b = src.find(key)?;
//     let key_and_after = &src[i_key_b..];
//     let i_value_e = key_and_after.find("\n")?;
//     let value = &key_and_after[..i_value_e];
//     Some(value.trim_start_matches(key).trim())
// }

fn parse_ssid(src: &str) -> String {
    let mut s = Vec::with_capacity(src.len());
    let mut i = 0;
    let bytes = src.as_bytes();
    while i < src.len() {
        let b = bytes[i];
        if b == b'\\' {
            let elem = &bytes[i + 2..i + 4];
            let elem_str = match std::str::from_utf8(elem) {
                Ok(s) => s,
                Err(_) => {
                    break;
                }
            };

            if let Ok(c) = u8::from_str_radix(elem_str, 16) {
                s.push(c);
            }
            i += 4;
        } else {
            s.push(b);
            i += 1;
        }
    }
    let encodings_to_try = vec![UTF_8, GBK, BIG5, EUC_KR];

    for encoding in encodings_to_try {
        let (out, _, has_err) = encoding.decode(&s);
        if !has_err {
            return out.to_string();
        }
    }

    String::new()
}

fn prase_section(src: &str, level: usize) -> HashMap<String, String> {
    let mut out = HashMap::new();
    let mut value = String::new();
    let mut key = String::new();

    let pattern = "\t".repeat(level);

    for line in src.lines() {
        if line.starts_with(pattern.as_str()) && line.chars().count() > level {
            let b = line.as_bytes()[level];
            if b != b'\t' {
                if !key.is_empty() {
                    out.insert(key.clone(), value.clone());
                    value = String::new();
                }
                let sp = line.split(":").collect::<Vec<_>>();
                key = sp[0].trim().to_string();
                value += pattern.as_str();
                value += sp[1].trim();
                continue;
            }
        }
        value += "\r\n";
        value += line;
    }

    out
}

#[cfg(test)]
mod test {
    use super::*;

    const SCAN_RESULT: &str = include_str!("../data/scan.txt");

    #[test]
    fn test_bss() {
        let out = BSS::parse_all(SCAN_RESULT).unwrap();
        assert_eq!(out[0].ssid, "IOT2_4G2");
        for one in out {
            println!("{}", one);
        }
    }

    // #[test]
    // fn test_section() {
    //     let src = r"BSS 04:f9:f8:aa:54:98(on wlxc43cb064be06)
    //     last seen: 7177304.561s [boottime]
    //     TSF: 11505523040 usec (0d, 03:11:45)
    //     RSN:     * Version: 1
    //              * Group cipher: CCMP
    //              * Pairwise ciphers: CCMP
    //              * Authentication suites: PSK
    //              * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000)
    //     RM enabled capabilities:
    //             Capabilities: 0x73 0xd0 0x00 0x00 0x0c
    //                     Link Measurement
    //                     Neighbor Report
    //                     Beacon Passive Measurement
    //                     Beacon Active Measurement
    //                     Beacon Table Measurement
    //                     LCI Measurement
    //                     Transmit Stream/Category Measurement
    //                     Triggered Transmit Stream/Category
    //                     FTM Range Report
    //                     Civic Location Measurement
    //             Nonoperating Channel Max Measurement Duration: 0
    //     ";

    //     let out = prase_section(src, 1);
    //     assert_eq!(out["TSF"], "11505523040 usec (0d, 03:11:45)");
    // }

    #[test]
    fn test_ssid_parse() {
        let s = "\\xce\\xc4\\xc1\\xd8_5G\\xce\\xc4\\xc1\\xd8";
        let ssid = parse_ssid(s);

        assert_eq!(ssid, "文霖_5G文霖");
    }
}
